
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#include "ModelsSummaryByLigand.h"

#include "extern/pugixml/src/pugixml.hpp"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <fstream>
#include <iostream>
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
using namespace std;
using namespace pugi;
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#define MAX_SIZE 500000
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
namespace ModelsSummary {
  
  bool EquivalentModels (TNode <ModelTotalResults>* model, void* ref) {
    
    ModelTotalResults* modelRef = (ModelTotalResults*)ref;

    return ((!strcmp(model->Info()->Name()->Out(), modelRef->Name()->Out())) && (!strcmp(model->Info()->Scheme()->Out(), modelRef->Scheme()->Out())) &&
            (!strcmp(model->Info()->Selection()->Out(), modelRef->Selection()->Out())))?true:false;

  }
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ModelsSummaryByLigand::ModelsSummaryByLigand () {

  this->ligandName                     = new String ();
  this->filenameOut                    = new String ();
  this->filenameIn                     = new String ();
  this->models                         = new TListE <ModelTotalResults> ();

  this->toString                       = new String ();

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ModelsSummaryByLigand::ModelsSummaryByLigand (const ModelsSummaryByLigand& modelsSummaryByLigand) {

  this->ligandName                     = new String (modelsSummaryByLigand.ligandName);
  this->filenameOut                    = new String (modelsSummaryByLigand.filenameOut);
  this->filenameIn                     = new String (modelsSummaryByLigand.filenameIn);
  this->models                         = new TListE <ModelTotalResults> (modelsSummaryByLigand.models);

  this->toString                       = new String (modelsSummaryByLigand.toString);

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ModelsSummaryByLigand::ModelsSummaryByLigand (const ModelsSummaryByLigand* modelsSummaryByLigand) {

  this->ligandName                     = new String (modelsSummaryByLigand->ligandName);
  this->filenameOut                    = new String (modelsSummaryByLigand->filenameOut);
  this->filenameIn                     = new String (modelsSummaryByLigand->filenameIn);
  this->models                         = new TListE <ModelTotalResults> (modelsSummaryByLigand->models);

  this->toString                       = new String (modelsSummaryByLigand->toString);

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ModelsSummaryByLigand::~ModelsSummaryByLigand () {

  if (this->ligandName)  delete this->ligandName;
  if (this->filenameOut) delete this->filenameOut;
  if (this->filenameIn)  delete this->filenameIn;
  if (this->models)      delete this->models;

  if (this->toString)    delete this->toString;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ModelsSummaryByLigand::LigandName (String* ligandName) {

  if (ligandName)
    *(this->ligandName) = *ligandName;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ModelsSummaryByLigand::FilenameOut (String* filenameOut) {

  if (filenameOut)
    *(this->filenameOut) = *filenameOut;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ModelsSummaryByLigand::FilenameIn (String* filenameIn) {

  if (filenameIn)
    *(this->filenameIn) = *filenameIn;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ModelsSummaryByLigand::Models (TListE <ModelTotalResults>* models) {

  if (models)
    *(this->models) = *models;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ModelsSummaryByLigand::LigandName (void) {

  return this->ligandName;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ModelsSummaryByLigand::FilenameOut (void) {

  return this->filenameOut;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ModelsSummaryByLigand::FilenameIn (void) {

  return this->filenameIn;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
TListE <ModelTotalResults>* ModelsSummaryByLigand::Models (void) {

  return this->models;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ModelsSummaryByLigand::ToString (void) {

  char buffer [MAX_SIZE];
  int  counter = 0;
  
  this->toString->In("");
  
  sprintf(buffer, "%s,,,,,,,,,,,,,,,,,,,,,,\n\0", this->ligandName->Out());
  this->toString->Add(buffer);
  
  this->toString->Add("Error on training data,,,,,,,,,,,,,,,,,,,,,,\n\0");
  this->toString->Add("Statistic variables,,,,,,,,,,,,,,,,,,,,,,\n\0");
  this->toString->Add(",,,,,,,,,,,,,,,,,,,,,,\n\0");
  this->toString->Add("No,Scheme,Number Attributes,Model,Correctly Classified Instances,Correctly Classified Percentage,Incorrectly Classified Instances,Incorrectly Classified Percentage,Kappa statistic,K&B Relative Info Score,K&B Information Score,K&B Information Score (bits/instance),Class complexity | order 0,Class complexity | order 0 (bits/instance),Class complexity | scheme,Class complexity | scheme (bits/instance),Complexity improvement,Complexity improvement (bits/instance),Mean absolute error,Root mean squared error,Relative absolute error,Root relative squared error,Total Number of Instances\n\0");

  this->models->SetInitial();
  while (this->models->SetNext()) {

    ModelTotalResults*    currentModel      = this->models->GetCurrent();
    ModelDetailStatistic* currentStatistic  = NULL;
    
    for (int i = 0; i < currentModel->HeaderClassifier()->Length(); i++)
      if (!strcmp(currentModel->HeaderClassifier()->Get(i)->Out(), "training-data")) {
        
        currentStatistic = currentModel->DetailClassifier()->Get(i);
        
        sprintf(buffer, "%d,%s,%d,%s,%d,%f,%d,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%d\n\0", ++counter
                                                                                                  , currentModel->Scheme()->Out()
                                                                                                  , currentModel->NumberAttributes()
                                                                                                  , currentModel->Name()->Out()
                                                                                                  , currentStatistic->CorrectlyClassifiedInstance()
                                                                                                  , currentStatistic->CorrectlyClassifiedPercentage()
                                                                                                  , currentStatistic->IncorrectlyClassifiedInstance()
                                                                                                  , currentStatistic->IncorrectlyClassifiedPercentage()
                                                                                                  , currentStatistic->Kappa()
                                                                                                  , currentStatistic->KBRelativeInfoScore()
                                                                                                  , currentStatistic->KBInfoScoreBits()
                                                                                                  , currentStatistic->KBInfoScoreBitsPerInstance()
                                                                                                  , currentStatistic->ClassComplexityOrder()
                                                                                                  , currentStatistic->ClassComplexityOrderPerInstance()
                                                                                                  , currentStatistic->ClassComplexityScheme()
                                                                                                  , currentStatistic->ClassComplexitySchemePerInstance()
                                                                                                  , currentStatistic->ComplexityImprovement()
                                                                                                  , currentStatistic->ComplexityImprovementPerInstance()
                                                                                                  , currentStatistic->MeanAbsoluteError()
                                                                                                  , currentStatistic->RmsError()
                                                                                                  , currentStatistic->RelativeAbsoluteError()
                                                                                                  , currentStatistic->RootRelativeSquareError()
                                                                                                  , currentStatistic->TotalNumberInstance());

        this->toString->Add(buffer);
        
      }

  }
  
  this->toString->Add(",,,,,,,,,,,,,,,,,,,\n\0");
  this->toString->Add("Classes (Error on training data),,,,,,,,,,,,,,,,,,,\n\0");
  this->toString->Add(",,,,,,,,,,,,,,,,,,,\n\0");
  this->toString->Add("No,Scheme,TP Rate(Bind),FP Rate(Bind),Precision(Bind),Recall(Bind),F-Measure(Bind),ROC Area(Bind),TP Rate(Not-Bind),FP Rate(Not-Bind),Precision(Not-Bind),Recall(Not-Bind),F-Measure(Not-Bind),ROC Area(Not-Bind),TP Rate(WAverage),FP Rate(WAverage),Precision(WAverage),Recall(WAverage),F-Measure(WAverage),ROC Area(WAverage)\n\0");

  this->models->SetInitial();
  while (this->models->SetNext()) {

    ModelTotalResults*    currentModel      = this->models->GetCurrent();
    ModelDetailStatistic* currentStatistic  = NULL;
    
    ModelDetailAccuracy*  bindClass         = NULL;
    ModelDetailAccuracy*  notbindClass      = NULL;
    ModelDetailAccuracy*  averageClass      = NULL;
    
    for (int i = 0; i < currentModel->HeaderClassifier()->Length(); i++)
      if (!strcmp(currentModel->HeaderClassifier()->Get(i)->Out(), "training-data")) {
        
        currentStatistic = currentModel->DetailClassifier()->Get(i);
        
        currentStatistic->DetailAccuracyByClass()->SetInitial();
        while (currentStatistic->DetailAccuracyByClass()->SetNext()) {
          if      (!strcmp(currentStatistic->DetailAccuracyByClass()->GetCurrent()->ClassName()->Out(), "bind")) 
            bindClass = currentStatistic->DetailAccuracyByClass()->GetCurrent(); 
          else if (!strcmp(currentStatistic->DetailAccuracyByClass()->GetCurrent()->ClassName()->Out(), "not-bind")) 
            notbindClass = currentStatistic->DetailAccuracyByClass()->GetCurrent(); 
          else if (!strcmp(currentStatistic->DetailAccuracyByClass()->GetCurrent()->ClassName()->Out(), "weightedAvg")) 
            averageClass = currentStatistic->DetailAccuracyByClass()->GetCurrent(); 
        }

        if (!bindClass || !notbindClass || !averageClass)
          return NULL;

        sprintf(buffer, "%d,%s,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f\n\0" , ++counter
                                                                                          , currentModel->Scheme()->Out()
                                                                                          , bindClass->TruePositiveRate()
                                                                                          , bindClass->FalsePositiveRate()
                                                                                          , bindClass->Precision()
                                                                                          , bindClass->Recall()
                                                                                          , bindClass->FMeasure()
                                                                                          , bindClass->RocArea()
                                                                                          , notbindClass->TruePositiveRate()
                                                                                          , notbindClass->FalsePositiveRate()
                                                                                          , notbindClass->Precision()
                                                                                          , notbindClass->Recall()
                                                                                          , notbindClass->FMeasure()
                                                                                          , notbindClass->RocArea()
                                                                                          , averageClass->TruePositiveRate()
                                                                                          , averageClass->FalsePositiveRate()
                                                                                          , averageClass->Precision()
                                                                                          , averageClass->Recall()
                                                                                          , averageClass->FMeasure()
                                                                                          , averageClass->RocArea());

        this->toString->Add(buffer);

      }
    
  }
  
  this->toString->Add("Confusion Matrix (Error on training data),,\n\0");
  this->toString->Add(",,\n\0");
  this->toString->Add("No,Scheme,Confusion Matrix\n\0");
  
  this->models->SetInitial();
  while (this->models->SetNext()) {

    ModelTotalResults*    currentResult = this->models->GetCurrent();
    ModelDetailStatistic* currentStatistic  = NULL;
    
    for (int i = 0; i < currentResult->HeaderClassifier()->Length(); i++)
      if (!strcmp(currentResult->HeaderClassifier()->Get(i)->Out(), "training-data")) {
        
        currentStatistic = currentResult->DetailClassifier()->Get(i);
    
        sprintf(buffer, "%d,%s,%s\n\0", ++counter, currentResult->Scheme()->Out(), currentStatistic->ToStringConfusionMatrix()->Out());
        this->toString->Add(buffer);

      }
    
  }
  
  this->toString->Add(",,,,,,,,,,,,,,,,,,,,,,\n\0");
  this->toString->Add(",,,,,,,,,,,,,,,,,,,,,,\n\0");

  this->toString->Add("Stratified cross-validation,,,,,,,,,,,,,,,,,,,,,,\n\0");
  this->toString->Add("Statistic variables,,,,,,,,,,,,,,,,,,,,,,\n\0");
  this->toString->Add(",,,,,,,,,,,,,,,,,,,,,,\n\0");
  this->toString->Add("No,Scheme,Number Attributes,Model,Correctly Classified Instances,Correctly Classified Percentage,Incorrectly Classified Instances,Incorrectly Classified Percentage,Kappa statistic,K&B Relative Info Score,K&B Information Score,K&B Information Score (bits/instance),Class complexity | order 0,Class complexity | order 0 (bits/instance),Class complexity | scheme,Class complexity | scheme (bits/instance),Complexity improvement,Complexity improvement (bits/instance),Mean absolute error,Root mean squared error,Relative absolute error,Root relative squared error,Total Number of Instances\n\0");

  this->models->SetInitial();
  while (this->models->SetNext()) {

    ModelTotalResults*    currentModel     = this->models->GetCurrent();
    ModelDetailStatistic* currentStatistic = NULL;
    
    for (int i = 0; i < currentModel->HeaderClassifier()->Length(); i++)
      if (!strcmp(currentModel->HeaderClassifier()->Get(i)->Out(), "cross-validation")) {
        
        currentStatistic = currentModel->DetailClassifier()->Get(i);
        
        sprintf(buffer, "%d,%s,%d,%s,%d,%f,%d,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%d\n\0", ++counter
                                                                                                  , currentModel->Scheme()->Out()
                                                                                                  , currentModel->NumberAttributes()
                                                                                                  , currentModel->Name()->Out()
                                                                                                  , currentStatistic->CorrectlyClassifiedInstance()
                                                                                                  , currentStatistic->CorrectlyClassifiedPercentage()
                                                                                                  , currentStatistic->IncorrectlyClassifiedInstance()
                                                                                                  , currentStatistic->IncorrectlyClassifiedPercentage()
                                                                                                  , currentStatistic->Kappa()
                                                                                                  , currentStatistic->KBRelativeInfoScore()
                                                                                                  , currentStatistic->KBInfoScoreBits()
                                                                                                  , currentStatistic->KBInfoScoreBitsPerInstance()
                                                                                                  , currentStatistic->ClassComplexityOrder()
                                                                                                  , currentStatistic->ClassComplexityOrderPerInstance()
                                                                                                  , currentStatistic->ClassComplexityScheme()
                                                                                                  , currentStatistic->ClassComplexitySchemePerInstance()
                                                                                                  , currentStatistic->ComplexityImprovement()
                                                                                                  , currentStatistic->ComplexityImprovementPerInstance()
                                                                                                  , currentStatistic->MeanAbsoluteError()
                                                                                                  , currentStatistic->RmsError()
                                                                                                  , currentStatistic->RelativeAbsoluteError()
                                                                                                  , currentStatistic->RootRelativeSquareError()
                                                                                                  , currentStatistic->TotalNumberInstance());

        this->toString->Add(buffer);

      }
    
  }
  
  this->toString->Add("Classes (Stratified cross-validation),,,,,,,,,,,,,,,,,,,\n\0");
  this->toString->Add(",,,,,,,,,,,,,,,,,,,\n\0");
  this->toString->Add("No,Scheme,TP Rate(Bind),FP Rate(Bind),Precision(Bind),Recall(Bind),F-Measure(Bind),ROC Area(Bind),TP Rate(Not-Bind),FP Rate(Not-Bind),Precision(Not-Bind),Recall(Not-Bind),F-Measure(Not-Bind),ROC Area(Not-Bind),TP Rate(WAverage),FP Rate(WAverage),Precision(WAverage),Recall(WAverage),F-Measure(WAverage),ROC Area(WAverage)\n\0");

  this->models->SetInitial();
  while (this->models->SetNext()) {

    ModelTotalResults*    currentModel     = this->models->GetCurrent();
    ModelDetailStatistic* currentStatistic = NULL;
    
    ModelDetailAccuracy*  bindClass     = NULL;
    ModelDetailAccuracy*  notbindClass  = NULL;
    ModelDetailAccuracy*  averageClass  = NULL;
    
    for (int i = 0; i < currentModel->HeaderClassifier()->Length(); i++)
      if (!strcmp(currentModel->HeaderClassifier()->Get(i)->Out(), "cross-validation")) {
        
        currentStatistic = currentModel->DetailClassifier()->Get(i);
        
        currentStatistic->DetailAccuracyByClass()->SetInitial();
        while (currentStatistic->DetailAccuracyByClass()->SetNext()) {
          if      (!strcmp(currentStatistic->DetailAccuracyByClass()->GetCurrent()->ClassName()->Out(), "bind")) 
            bindClass = currentStatistic->DetailAccuracyByClass()->GetCurrent(); 
          else if (!strcmp(currentStatistic->DetailAccuracyByClass()->GetCurrent()->ClassName()->Out(), "not-bind")) 
            notbindClass = currentStatistic->DetailAccuracyByClass()->GetCurrent(); 
          else if (!strcmp(currentStatistic->DetailAccuracyByClass()->GetCurrent()->ClassName()->Out(), "weightedAvg")) 
            averageClass = currentStatistic->DetailAccuracyByClass()->GetCurrent(); 
        }

        if (!bindClass || !notbindClass || !averageClass)
          return NULL;

        sprintf(buffer, "%d,%s,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f\n\0" , ++counter
                                                                                          , currentModel->Scheme()->Out()
                                                                                          , bindClass->TruePositiveRate()
                                                                                          , bindClass->FalsePositiveRate()
                                                                                          , bindClass->Precision()
                                                                                          , bindClass->Recall()
                                                                                          , bindClass->FMeasure()
                                                                                          , bindClass->RocArea()
                                                                                          , notbindClass->TruePositiveRate()
                                                                                          , notbindClass->FalsePositiveRate()
                                                                                          , notbindClass->Precision()
                                                                                          , notbindClass->Recall()
                                                                                          , notbindClass->FMeasure()
                                                                                          , notbindClass->RocArea()
                                                                                          , averageClass->TruePositiveRate()
                                                                                          , averageClass->FalsePositiveRate()
                                                                                          , averageClass->Precision()
                                                                                          , averageClass->Recall()
                                                                                          , averageClass->FMeasure()
                                                                                          , averageClass->RocArea());

        this->toString->Add(buffer);

      }
    
  }
  
  this->toString->Add("Confusion Matrix (Stratified cross-validation),,\n\0");
  this->toString->Add(",,\n\0");
  this->toString->Add("No,Scheme,Confusion Matrix\n\0");
  
  this->models->SetInitial();
  while (this->models->SetNext()) {

    ModelTotalResults*    currentModel     = this->models->GetCurrent();
    ModelDetailStatistic* currentStatistic = NULL;
    
    for (int i = 0; i < currentModel->HeaderClassifier()->Length(); i++)
      if (!strcmp(currentModel->HeaderClassifier()->Get(i)->Out(), "cross-validation")) {
        
        currentStatistic = currentModel->DetailClassifier()->Get(i);
    
        sprintf(buffer, "%d,%s,%s\n\0", ++counter, currentModel->Scheme()->Out(), currentStatistic->ToStringConfusionMatrix()->Out());
        this->toString->Add(buffer);

      }
  }
  
  return this->toString;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ModelsSummaryByLigand::LoadXMLFile (void) {

  if (!this->filenameIn || !this->filenameIn->Length())
    return false;
  
  xml_document doc;
  
  if (!doc.load_file(this->filenameIn->Out())) 
    return false;  
  
  this->ligandName->In(doc.child_value("ligand"));
  
  for (xml_node classifier = doc.child("classifier"); classifier; classifier = classifier.next_sibling()) {

    ModelTotalResults* newModelTotalResults = new ModelTotalResults ();

    for (xml_attribute attr = classifier.first_attribute(); attr; attr = attr.next_attribute()) {
      if      (!strcmp(attr.name(), "name"))       newModelTotalResults->Name()->In(attr.value());
      else if (!strcmp(attr.name(), "scheme"))     newModelTotalResults->Scheme()->In(attr.value()); 
      else if (!strcmp(attr.name(), "numberattr")) newModelTotalResults->NumberAttributes(atoi(attr.value())); 
    }

    newModelTotalResults->Selection()->In(classifier.child_value("selection"));
    newModelTotalResults->DetailsModel()->In(classifier.child_value("description"));
    newModelTotalResults->Model()->In(classifier.child_value("model"));

    for (xml_node statistic = classifier.child("statistics").first_child(); statistic; statistic = statistic.next_sibling()) {

        String                data (statistic.child_value());
        ModelDetailStatistic* newModelDetailStatistic = new ModelDetailStatistic ();

        if (newModelDetailStatistic->LoadFromData(&data)) {
          newModelTotalResults->HeaderClassifier()->Add(new String(statistic.attribute("name").value()));
          newModelTotalResults->DetailClassifier()->Add(newModelDetailStatistic);
        }
        else
          delete newModelDetailStatistic;

    }
    
    this->models->Add(newModelTotalResults);

  }
  
  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ModelsSummaryByLigand::AddModelFromFile (String* filename, String* name, String* scheme, String* selection) {

  if (!filename || !filename->Length() || !name      || !name->Length() ||
      !scheme   || !scheme->Length()   || !selection || !selection->Length())
    return false;

  ModelTotalResults* newModelTotalResults = new ModelTotalResults ();
  
  newModelTotalResults->FilenameIn(filename);
  
  if (newModelTotalResults->LoadFromFile()) {
    
    newModelTotalResults->Name(name);
    newModelTotalResults->Scheme(scheme);
    newModelTotalResults->Selection(selection);
    
    ModelTotalResults* searchModelTotalResults = this->models->FirstOcurrence(ModelsSummary::EquivalentModels, newModelTotalResults);
    
    if (!searchModelTotalResults)
      this->models->Add(newModelTotalResults);
    else if (newModelTotalResults) 
      delete newModelTotalResults;
    
  }
  else if (newModelTotalResults) delete newModelTotalResults;
  
  return true;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ModelsSummaryByLigand::AddModelFromData (String* data, String* name, String* scheme, String* selection) {

  if (!data   || !data->Length()   || !name      || !name->Length() ||
      !scheme || !scheme->Length() || !selection || !selection->Length())
    return false;

  ModelTotalResults* newModelTotalResults = new ModelTotalResults ();
  
  if (newModelTotalResults->LoadFromData(data)) {
    
    newModelTotalResults->Name(name);
    newModelTotalResults->Scheme(scheme);
    newModelTotalResults->Selection(selection);
    
    ModelTotalResults* searchModelTotalResults = this->models->FirstOcurrence(ModelsSummary::EquivalentModels, newModelTotalResults);
    
    if (!searchModelTotalResults)
      this->models->Add(newModelTotalResults);
    else if (newModelTotalResults) 
      delete newModelTotalResults;
    
  }
  else if (newModelTotalResults) 
    delete newModelTotalResults;
  
  return true;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ModelsSummaryByLigand::ClearModels (void) {

  this->models->Clear();

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ModelsSummaryByLigand::WriteSummary (void) {

  if (!this->filenameOut || !this->filenameOut->Length())
    return false;
  
  ofstream file (this->filenameOut->Out());
  
  if (!file.is_open())
    return false;
  
  file << this->ToString()->Out() << endl;
  
  file.close();
  
  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ModelsSummaryByLigand::WriteXMLFile (void) {

  if (!this->filenameOut || !this->filenameOut->Length())
    return false;
  
  xml_document doc;
  xml_node     node_ligand = doc.append_child("ligand");

  node_ligand.append_child(node_pcdata).set_value(this->PrepareAsXMLContent(this->ligandName)->Out());
  
  this->models->SetInitial();
  while (this->models->SetNext()) {

    ModelTotalResults* currentModelTotalResults = this->models->GetCurrent();

    xml_node node_classifier   = doc.append_child("classifier");

    node_classifier.append_attribute("name")       = this->PrepareAsXMLContent(currentModelTotalResults->Name())->Out();
    node_classifier.append_attribute("scheme")     = this->PrepareAsXMLContent(currentModelTotalResults->Scheme())->Out();
    node_classifier.append_attribute("numberattr") = currentModelTotalResults->NumberAttributes();
    
    xml_node node_selection    = node_classifier.append_child("selection");
    node_selection.append_child(node_pcdata).set_value(this->PrepareAsXMLContent(currentModelTotalResults->Selection())->Out());

    xml_node node_description  = node_classifier.append_child("description");
    node_description.append_child(node_pcdata).set_value(this->PrepareAsXMLContent(currentModelTotalResults->DetailsModel())->Out());

    xml_node node_model        = node_classifier.append_child("model");
    node_model.append_child(node_pcdata).set_value(this->PrepareAsXMLContent(currentModelTotalResults->Model())->Out());

    xml_node node_statistics   = node_classifier.append_child("statistics");

    currentModelTotalResults->HeaderClassifier()->SetInitial();
    currentModelTotalResults->DetailClassifier()->SetInitial();
    while (currentModelTotalResults->HeaderClassifier()->SetNext() && currentModelTotalResults->DetailClassifier()->SetNext()) {
      
      xml_node node_statistic = node_statistics.append_child("statistic");

      node_statistic.append_attribute("name") = this->PrepareAsXMLContent(currentModelTotalResults->HeaderClassifier()->GetCurrent())->Out();
      node_statistic.append_child(node_pcdata).set_value(this->PrepareAsXMLContent(currentModelTotalResults->DetailClassifier()->GetCurrent()->ToString(6))->Out());

    }
    
  }
  
  return doc.save_file(this->filenameOut->Out());

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String ModelsSummaryByLigand::ToCVSTableHeader (void) {
  
  String textOut;
  
  textOut.In("Ligand,Scheme,Number of Attributes,Number of Instances,Correctly Classified (%),Bind Class,,,Not-Bind Class,,,ROC Area\n\0");
  textOut.Add(",,,,,TP Rate,FP Rate,Precision,TP Rate,FP Rate,Precision,\n\0");

  return textOut;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ModelsSummaryByLigand::ToCVSTable (String* headerClassifier) {
  
  this->toString->In("");

  if (!headerClassifier || !headerClassifier->Length())
    return this->toString;

  char buffer [MAX_SIZE];
  
  this->models->SetInitial();
  while (this->models->SetNext()) {
    
    ModelTotalResults* currentModel = this->models->GetCurrent();
    
    for (int i = 0; i < currentModel->HeaderClassifier()->Length(); i++)
      if (!strcmp(currentModel->HeaderClassifier()->Get(i)->Out(), headerClassifier->Out())) {
        
        ModelDetailStatistic* currentStatistic = currentModel->DetailClassifier()->Get(i);
        
        ModelDetailAccuracy*  bindClass        = NULL;
        ModelDetailAccuracy*  notbindClass     = NULL;
        
        currentStatistic->DetailAccuracyByClass()->SetInitial();
        while (currentStatistic->DetailAccuracyByClass()->SetNext()) {
          if      (!strcmp(currentStatistic->DetailAccuracyByClass()->GetCurrent()->ClassName()->Out(), "bind")) 
            bindClass    = currentStatistic->DetailAccuracyByClass()->GetCurrent(); 
          else if (!strcmp(currentStatistic->DetailAccuracyByClass()->GetCurrent()->ClassName()->Out(), "not-bind")) 
            notbindClass = currentStatistic->DetailAccuracyByClass()->GetCurrent(); 
        }

        if (!bindClass || !notbindClass) {
          this->toString->In("");
          return this->toString;
        }
        
        sprintf(buffer, "%s,%40s,%d,%d,%0.2f %%,%0.2f,%0.2f,%0.2f,%0.2f,%0.2f,%0.2f,%0.2f\n\0" , this->ligandName->Out()
                                                                                               , currentModel->Scheme()->Out()
                                                                                               , currentModel->NumberAttributes()
                                                                                               , currentStatistic->CorrectlyClassifiedInstance() + currentStatistic->IncorrectlyClassifiedInstance() 
                                                                                               , currentStatistic->CorrectlyClassifiedPercentage()
                                                                                               , bindClass->TruePositiveRate()
                                                                                               , bindClass->FalsePositiveRate()
                                                                                               , bindClass->Precision()
                                                                                               , notbindClass->TruePositiveRate()
                                                                                               , notbindClass->FalsePositiveRate()
                                                                                               , notbindClass->Precision()
                                                                                               , notbindClass->RocArea());
        
        this->toString->Add(buffer);
        
        break;
        
      }  

  }  

  return this->toString;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ModelsSummaryByLigand::ToCVSTable (String* scheme, String* headerClassifier) {
  
  this->toString->In("");

  if (!scheme || !scheme->Length() || !headerClassifier || !headerClassifier->Length())
    return this->toString;

  char buffer [MAX_SIZE];
  
  this->models->SetInitial();
  while (this->models->SetNext()) {
    
    ModelTotalResults* currentModel = this->models->GetCurrent();
    
    if (!strcmp(currentModel->Scheme()->Out(), scheme->Out())) {
      
      for (int i = 0; i < currentModel->HeaderClassifier()->Length(); i++)
        if (!strcmp(currentModel->HeaderClassifier()->Get(i)->Out(), headerClassifier->Out())) {

          ModelDetailStatistic* currentStatistic = currentModel->DetailClassifier()->Get(i);

          ModelDetailAccuracy*  bindClass        = NULL;
          ModelDetailAccuracy*  notbindClass     = NULL;

          currentStatistic->DetailAccuracyByClass()->SetInitial();
          while (currentStatistic->DetailAccuracyByClass()->SetNext()) {
            if      (!strcmp(currentStatistic->DetailAccuracyByClass()->GetCurrent()->ClassName()->Out(), "bind")) 
              bindClass    = currentStatistic->DetailAccuracyByClass()->GetCurrent(); 
            else if (!strcmp(currentStatistic->DetailAccuracyByClass()->GetCurrent()->ClassName()->Out(), "not-bind")) 
              notbindClass = currentStatistic->DetailAccuracyByClass()->GetCurrent(); 
          }

          if (!bindClass || !notbindClass) {
            this->toString->In("");
            return this->toString;
          }

          sprintf(buffer, "%s,%40s,%d,%d,%0.2f %%,%0.2f,%0.2f,%0.2f,%0.2f,%0.2f,%0.2f,%0.2f\n\0" , this->ligandName->Out()
                                                                                                 , currentModel->Scheme()->Out()
                                                                                                 , currentModel->NumberAttributes()
                                                                                                 , currentStatistic->CorrectlyClassifiedInstance() + currentStatistic->IncorrectlyClassifiedInstance() 
                                                                                                 , currentStatistic->CorrectlyClassifiedPercentage()
                                                                                                 , bindClass->TruePositiveRate()
                                                                                                 , bindClass->FalsePositiveRate()
                                                                                                 , bindClass->Precision()
                                                                                                 , notbindClass->TruePositiveRate()
                                                                                                 , notbindClass->FalsePositiveRate()
                                                                                                 , notbindClass->Precision()
                                                                                                 , notbindClass->RocArea());

          this->toString->Add(buffer);

          break;

        }  

    }

  }  

  return this->toString;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String ModelsSummaryByLigand::ToLatexBeginDoc (void) {
  
  String textOut;
  
  textOut.In("\\documentclass[a4paper]{article}\n\n\0");
  textOut.Add("\\usepackage{graphicx}\n\\usepackage{array}\n\\usepackage{multirow}\n\\usepackage{chngpage}\n\\usepackage{booktabs}\n\n\0");
  textOut.Add("\\begin{document}\n\n\0");

  return textOut;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String ModelsSummaryByLigand::ToLatexEndDoc (void) {
  
  String textOut;
  
  textOut.In("\nA reference to Table \\ref{tab:template}.\n\n\0");
  textOut.Add("\\end{document}\n\n\0");

  return textOut;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String ModelsSummaryByLigand::ToLatexBeginTable (void) {
  
  String textOut;
  
  textOut.In("\n\\begin{table}\n\\scalebox{0.6}{\n\\def\\arraystretch{1.5}\n\\hspace{-3.5cm}\n\\begin{tabular}{| c c c c c c c c c c c c |}\n\n\0");

  return textOut;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String ModelsSummaryByLigand::ToLatexEndTable (void) {

  String textOut;
  
  textOut.In("\n\\hline\n\n\\end{tabular}\n}\n\\caption{Table caption text}\n\\label{tab:template}\n\\end{table}\n\n\0");

  return textOut;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String ModelsSummaryByLigand::ToLatexTableHeader (void) {

  String textOut;
  
  textOut.In("\n\\hline\n\0");
  textOut.Add("\\multirow{2}{*}{Ligand} & \\multirow{2}{*}{Scheme} & Number of & Number of & \\% Correctly & \\multicolumn{3}{c}{Bind Class} & \0");
  textOut.Add("\\multicolumn{3}{c}{Not-Bind Class} & \\multirow{2}{*}{ROC Area} \\\\\n\0");
  textOut.Add("\\cline{6-11}\n\0");
  textOut.Add("& & Attributes & Instances & Classified & TP Rate & FP Rate & Precision & TP Rate & FP Rate & Precision & \\\\\n\0");
  textOut.Add("\\hline\n\n\0");

  return textOut;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ModelsSummaryByLigand::ToLatexTableContent (String* headerClassifier) {
  
  this->toString->In("");

  if (!headerClassifier || !headerClassifier->Length())
    return this->toString;

  char buffer [MAX_SIZE];
  
  this->models->SetInitial();
  while (this->models->SetNext()) {
    
    ModelTotalResults* currentModel = this->models->GetCurrent();
    
    for (int i = 0; i < currentModel->HeaderClassifier()->Length(); i++)
      if (!strcmp(currentModel->HeaderClassifier()->Get(i)->Out(), headerClassifier->Out())) {
        
        ModelDetailStatistic* currentStatistic = currentModel->DetailClassifier()->Get(i);
        
        ModelDetailAccuracy*  bindClass        = NULL;
        ModelDetailAccuracy*  notbindClass     = NULL;
        
        currentStatistic->DetailAccuracyByClass()->SetInitial();
        while (currentStatistic->DetailAccuracyByClass()->SetNext()) {
          if      (!strcmp(currentStatistic->DetailAccuracyByClass()->GetCurrent()->ClassName()->Out(), "bind")) 
            bindClass    = currentStatistic->DetailAccuracyByClass()->GetCurrent(); 
          else if (!strcmp(currentStatistic->DetailAccuracyByClass()->GetCurrent()->ClassName()->Out(), "not-bind")) 
            notbindClass = currentStatistic->DetailAccuracyByClass()->GetCurrent(); 
        }

        if (!bindClass || !notbindClass) {
          this->toString->In("");
          return this->toString;
        }
        
        sprintf(buffer, "%s & %40s & %d & %d & %0.2f\\%% & %0.2f & %0.2f & %0.2f & %0.2f & %0.2f & %0.2f & %0.2f\\\\\n\0" , this->ligandName->Out()
                                                                                                                          , currentModel->Scheme()->Out()
                                                                                                                          , currentModel->NumberAttributes()
                                                                                                                          , currentStatistic->CorrectlyClassifiedInstance() + currentStatistic->IncorrectlyClassifiedInstance() 
                                                                                                                          , currentStatistic->CorrectlyClassifiedPercentage()
                                                                                                                          , bindClass->TruePositiveRate()
                                                                                                                          , bindClass->FalsePositiveRate()
                                                                                                                          , bindClass->Precision()
                                                                                                                          , notbindClass->TruePositiveRate()
                                                                                                                          , notbindClass->FalsePositiveRate()
                                                                                                                          , notbindClass->Precision()
                                                                                                                          , notbindClass->RocArea());
        
        this->toString->Add(buffer);
        
        break;
        
      }  

  }  

  return this->toString;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ModelsSummaryByLigand::ToLatexTableContent (String* scheme, String* headerClassifier) {
  
  this->toString->In("");
  
  if (!scheme || !scheme->Length() || !headerClassifier || !headerClassifier->Length())
    return this->toString;

  char buffer [MAX_SIZE];
  
  this->models->SetInitial();
  while (this->models->SetNext()) {
    
    ModelTotalResults* currentModel = this->models->GetCurrent();
    
    if (!strcmp(currentModel->Scheme()->Out(), scheme->Out())) {

      for (int i = 0; i < currentModel->HeaderClassifier()->Length(); i++)
        if (!strcmp(currentModel->HeaderClassifier()->Get(i)->Out(), headerClassifier->Out())) {

          ModelDetailStatistic* currentStatistic = currentModel->DetailClassifier()->Get(i);

          ModelDetailAccuracy*  bindClass        = NULL;
          ModelDetailAccuracy*  notbindClass     = NULL;

          currentStatistic->DetailAccuracyByClass()->SetInitial();
          while (currentStatistic->DetailAccuracyByClass()->SetNext()) {
            if      (!strcmp(currentStatistic->DetailAccuracyByClass()->GetCurrent()->ClassName()->Out(), "bind")) 
              bindClass    = currentStatistic->DetailAccuracyByClass()->GetCurrent(); 
            else if (!strcmp(currentStatistic->DetailAccuracyByClass()->GetCurrent()->ClassName()->Out(), "not-bind")) 
              notbindClass = currentStatistic->DetailAccuracyByClass()->GetCurrent(); 
          }

          if (!bindClass || !notbindClass) {
            this->toString->In("");
            return this->toString;
          }

          sprintf(buffer, "%s & %40s & %d & %d & %0.2f\\%% & %0.2f & %0.2f & %0.2f & %0.2f & %0.2f & %0.2f & %0.2f\\\\\n\0" , this->ligandName->Out()
                                                                                                                            , currentModel->Scheme()->Out()
                                                                                                                            , currentModel->NumberAttributes()
                                                                                                                            , currentStatistic->CorrectlyClassifiedInstance() + currentStatistic->IncorrectlyClassifiedInstance() 
                                                                                                                            , currentStatistic->CorrectlyClassifiedPercentage()
                                                                                                                            , bindClass->TruePositiveRate()
                                                                                                                            , bindClass->FalsePositiveRate()
                                                                                                                            , bindClass->Precision()
                                                                                                                            , notbindClass->TruePositiveRate()
                                                                                                                            , notbindClass->FalsePositiveRate()
                                                                                                                            , notbindClass->Precision()
                                                                                                                            , notbindClass->RocArea());

          this->toString->Add(buffer);

          break;

        }  
      
    }

  }  

  return this->toString;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ModelsSummaryByLigand::PrepareAsXMLContent (String* content) {
  
  if (!content) return NULL;
  
  for (long i = 0; i < content->Length(); i++) {
    if      ((*content)[i] == '<') content->In('{', i);
    else if ((*content)[i] == '>') content->In('}', i);
    else if ((*content)[i] == '&') content->In('^', i);
  }
  
  return content;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ModelsSummaryByLigand ModelsSummaryByLigand::operator= (const ModelsSummaryByLigand& modelsSummaryByLigand) {

  *(this->ligandName)  = *(modelsSummaryByLigand.ligandName);
  *(this->filenameOut) = *(modelsSummaryByLigand.filenameOut);
  *(this->filenameIn)  = *(modelsSummaryByLigand.filenameIn);
  *(this->models)      = *(modelsSummaryByLigand.models);

  *(this->toString)    = *(modelsSummaryByLigand.toString);

  return *this;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
